Is Perl5's Object System Really That Bad?

david m on 2009-12-22T14:12:18

I come from a C++ background and I'll admit that Perl's object system really confused me initially. The camel book - bless it's heart - really doesn't explain things well at all. I've occasionally thought about learning Moose, but whenever I try to wrap my head around it, I find myself more interested in just using traditional hash-based or inside-out objects. The notion of requiring thousands of lines of code to use an object system just doesn't sit well with me. I'd rather use an object system that's 'native' to the language.

Hash-based objects are, of course, the traditional object system in Perl. So far, they're all I've actually used in 'production' code. As you can imagine, the class structure for most of my code is pretty simple, so sticking with the hash-based object system, despite its deficiencies, has worked out well for me.

The C++ programmer in me really likes the encapsulated feeling of inside-out objects. (I know, the data is not REALLY totally private, but it's good enough for me.) The extra rigmarole required to get good memory management and threading-aware capabilities is somewhat annoying, but I don't really write the sorts of scripts for which these would be major concerns. Still, for some reason, I've never actually played around with an inside-out class system. Maybe I'll use it in my next project, just so I can get a feel for it...

Anyway, the purpose of this post was to state that I, for my part, have really come to enjoy Perl5's traditional object system. (This applies to both hash-based and inside-out objects.) I say this because I have figured out how to use it in ways I never could have used C++'s object system. Anybody well versed with the Gang of Four will say that I could have just used a Decorator class for my purposes, and they're right, but the amazing thing to me is that Perl5's object system supports decorator-like behavior 'out-of-the-box,' without any special setup.

Let me illustrate what I mean. I've been working on a simulator for the Kuramoto model in which I want to observe the behavior of subsets of my given population of oscillators. I wrote a class to hold the simulation internals and a class to take subsets and finally a class that manages a collection of subsets, which I call a Stack. For purposes of my work, the Stack is really the class of interest. My directory structure looks something like this:

My/Compute.pm - simulation internals
My/Slices.pm - sub-set capabilities
My/Stack.pm - manages collections of sub-sets
My/Animator.pm - enables SDL animations
My/Record.pm - enables data recording
My/SaveTo/Piddle.pm - enables saving data to a PDL piddle

Notice those bits that talk about enabling? The Animator, Record, and SaveTo::Piddle modules extend My::Stack in useful ways by adding functions to the My::Stack package and therefore to Stack objects. The great thing is that if I want to record data, I simply use My::Record; somewhere in my script, and if I don't want to record data, I don't use it. Similarly, if I ever want to extend my recording capabilities with a grander extension, I can write a new one in, say, My::Record::Advanced and the use it instead. This is great because it allows me to incrementally add capabilities to my Stack class without having to subclass it or modify the internals of the original class, or break pre-existing code.

Does this system have deficiencies? You bet it does, especially the way I've used it. In particular, this system is very difficult to use as a base class for other systems if I use two non-orthogonal packages, like My::Reocrd and My::Record::Advanced. This is because both modules would add conflicting definitions for the start_recording function. The result is that whenever using this sort of arrangement, you should try as best you can to keep each extension orthogonal to previous extensions. This is not reasonable for large projects, in which case you should use the Decorator design pattern, but for small projects like mine it is quite reasonable and leads to really simple code that Gets the Job Done.

This idea of extending the class with additional modules came to me by studying the PDL source code. I really like this capability. If poorly managed, it could of course lead to major headaches, but if you have a broad but shallow (and orthogonal) set of capabilities that you want to add to a class, like with PDL, this is a great way of doing it. This is a perfect example of Perl getting out of my way just letting me write code.


Moose

tempire on 2009-12-22T16:22:20

In comparison to a compiled language, I'm all for Perl's default objects. They're amazing. I had no idea how much manual labor I was doing, however, until I took a cursory look at other scripting languages. Since I focus on scripting, saving time is my first priority.

The biggest problem with implementing Moose, besides lack of nativity (timely reference! *sigh*), is there has yet to be a truly simple tutorial that brings you from 0 to 60 without days of wrapping your head around Class::MOP. Even the Moose homepage is confusing - lots of information, but no direction.

Moose, or even better, MooseX::Declare, should be included in basic Perl5 tutorials going forward.